<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>5.2.1-deleteProperty</title>
</head>

<body>
    <div id="container"></div>
</body>
<script>
    /**
     * 到底什么是对象呢？
     * - 根据 ECMAScript 规范，在 JavaScript 中有两种对象，
     *    其中一种叫作常规对象（ordinary object），
     *    另一种叫作异质对象（exotic object）。
     *    这两种对象包含了 JavaScript 世界中的所有对象，
     *    任何不属于常规对象的对象都是异质对象
     * 
     * 以下三点要求的对象就是常规对象, 而所有不符合这三点要求的对象都是异质对象
     * - 一个对象必须部署 11 个必要的内部方法， 必须使用 ECMA 规范 10.1.x 节给出的定义实现
     * -- [[GetPrototypeOf]]：获取对象的原型。[[GetPrototypeOf]] 的返回值必须是对象或 null
     * -- [[SetPrototypeOf]]：设置对象的原型。[[SetPrototypeOf]] 的返回值必须是布尔值
     * -- [[IsExtensible]]：判断对象是否可扩展。[[IsExtensible]] 的返回值必须是布尔值
     * -- [[PreventExtensions]]：阻止对象扩展。[[PreventExtensions]] 的返回值必须是布尔值
     * -- [[GetOwnProperty]]：获取对象的自身属性。[[GetOwnProperty]] 的返回值必须是属性描述符对象或 undefined
     * -- [[DefineOwnProperty]]：定义对象的自身属性。[[DefineOwnProperty]] 的返回值必须是布尔值
     * -- [[HasProperty]]：判断对象是否包含某个属性。[[HasProperty]] 的返回值必须是布尔值
     * -- [[Get]]：获取对象的属性值。[[Get]] 的返回值可以是任意类型
     * -- [[Set]]：设置对象的属性值。[[Set]] 的返回值必须是布尔值
     * -- [[Delete]]：删除对象的属性。[[Delete]] 的返回值必须是布尔值
     * -- [[OwnPropertyKeys]]：获取对象的属性列表。[[OwnPropertyKeys]] 的返回值必须是一个数组
     * - 对于内部方法 [[Call]]，必须使用 ECMA 规范 10.2.1 节给出的定义实现
     * - 对于内部方法 [[Construct]]，必须使用 ECMA 规范 10.2.2 节给出的定义实现
     * 
     * 
     * 
     * 如何区分一个对象是普通对象还是函数呢？
     *  一个对象在什么情况下才能作为函数调用呢？
     * - 通过内部方法和内部槽来区分对象，
     *    例如函数对象会部署内部方法 [[Call]]，而普通对象则不会。
     * 
     * 内部方法具有多态性，这是什么意思呢？
     * - 类似于面向对象里多态的概念。
     *    这就是说，不同类型的对象可能部署了相同的内部方法，却具有不同的逻辑。
     * - 例如，普通对象和 Proxy 对象都部署了 [[Get]] 这个内部方法，
     *    但它们的逻辑是不同的
     * 
     * 
     * Proxy 对象
     * 
     * - 由于 Proxy 对象的内部方法 [[Get]] 没有使用 ECMA 规范的 10.1.8 节给出的定义实现，
     *    所以Proxy 是一个异质对象。
     * 
     * - Proxy 内部方法 [[Get]] 的实现 与 普通对象
     * -- 这里体现了内部方法的多态性，即不同的对象部署相同的内部方法，但它们的行为可能不同
     * -- 具体的不同体现在，如果在创建代理对象时没有指定对应的拦截函数，
     *     例如没有指定 get() 拦截函数，那么当我们通过代理对象访问属性值时，
     *     代理对象的内部方法 [[Get]] 会调用原始对象的内部方法 [[Get]] 来获取属性值，
     *     这其实就是代理透明性质。
     * 
     * - 创建代理对象时指定的拦截函数，
     *    实际上是用来自定义代理对象本身的内部方法和行为的，
     *    而不是用来指定被代理对象的内部方法和行为的
     * 
     * - [[Call]] 和 [[Construct]] 这两个内部方法只有当被代理的对象是函数和构造函数时才会部署。
     * 
     * - 要拦截删除属性操作时，可以使用 deleteProperty 拦截函数实现
     * -- 需要强调的是，
     *     deleteProperty 实现的是代理对象 p 的内部方法和行为，
     *     所以为了删除被代理对象上的属性值，
     *     我们需要使用Reflect.deleteProperty(target, key) 来完成
     * 
     * */

const obj = { foo: 1 }

const p = new Proxy(obj, {
  deleteProperty(target, key) {
    return Reflect.deleteProperty(target, key)
  }
})

console.log(p.foo) // 1
delete p.foo
console.log(p.foo) // undefined
console.log(obj.foo) // undefined
</script>

</html>